// @ts-strict-ignore
import compact from "lodash/compact";

import { FieldType, FilterElement, IFilter, InvalidFilters, ValidationErrorCode } from "./types";

export const getByName = (nameToCompare: string) => (obj: { name: string }) =>
  obj.name === nameToCompare;

export const isAutocompleteFilterFieldValid = function <T extends string>({
  value,
}: FilterElement<T>) {
  return !!compact(value).length;
};

export const isNumberFilterFieldValid = function <T extends string>({ value }: FilterElement<T>) {
  const [min, max] = value;

  if (!min && !max) {
    return false;
  }

  return true;
};

export const isFilterFieldValid = function <T extends string>(filter: FilterElement<T>) {
  const { type } = filter;

  switch (type) {
    case FieldType.number:
      return isNumberFilterFieldValid(filter);
    case FieldType.boolean:
    case FieldType.autocomplete:
      return isAutocompleteFilterFieldValid(filter);
    case FieldType.options:
      return !!filter.value[0];

    default:
      return true;
  }
};

export const isFilterValid = function <T extends string>(filter: FilterElement<T>) {
  const { required, active } = filter;

  if (!required && !active) {
    return true;
  }

  return isFilterFieldValid(filter);
};

export const extractInvalidFilters = function <T extends string>(
  filtersData: Array<FilterElement<T>>,
  filtersDataStructure: Array<FilterElement<T>>,
): InvalidFilters<T> {
  return filtersDataStructure.reduce((invalidFilters, { name, multipleFields, dependencies }) => {
    const filter = filtersData.find(getByName(name));
    let errors: string[] = [];

    const shouldExtractChildrenFields = filter.active && !!multipleFields?.length;

    // if filter is inactive we skip entire validation
    if (!filter.active) {
      return invalidFilters;
    }

    if (!isFilterValid(filter)) {
      errors.push(ValidationErrorCode.VALUE_REQUIRED);
    }

    if (shouldExtractChildrenFields) {
      const multipleFieldErrors = multipleFields
        .map(field => {
          const filter = filtersData.find(getByName(field.name));

          return { ...filter, active: true };
        })
        .filter(el => !isFilterValid(el))
        .map(({ name }) => name);

      errors = [...errors, ...multipleFieldErrors];
    }

    // check if filter depends on other filters
    if (dependencies?.length > 0) {
      const deps = dependencies
        .map(name => {
          const filter = filtersData.find(getByName(name));

          return { ...filter, required: true };
        })
        .filter(el => !isFilterValid(el));

      if (deps.length > 0) {
        errors.push(ValidationErrorCode.DEPENDENCIES_MISSING);
      }
    }

    if (errors.length === 0) {
      return invalidFilters;
    }

    return {
      ...invalidFilters,
      [name]: errors,
    };
  }, {} as InvalidFilters<T>);
};

export const getSelectedFiltersAmount = <TFilterKeys extends string = string>(
  menu: IFilter<TFilterKeys>,
  data: Array<FilterElement<string>>,
) =>
  menu.reduce((acc, filterElement) => {
    const dataFilterElement = data.find(({ name }) => name === filterElement.name);

    if (!dataFilterElement) {
      return acc;
    }

    return acc + (dataFilterElement.active ? 1 : 0);
  }, 0);
